
import pygtk
pygtk.require('2.0')
import gtk
import math

import _emulator

# TODO
#
# Add font setup to the text() method
# Implement event handling
# Implement blit() method

#Events
EEventKeyUp = 0x01
EEventKeyDown = 0x02
EEventKey = 0x03

class Canvas:
    """Canvas is a UI control that provides a drawable area on the screen and support for handling raw key
    events. 
    
    Canvas supports the standard drawing methods.
    """
    def __init__(self, redraw_callback = None, event_callback = None, resize_callback=None):
        """ """
	
	self._redraw_callback = redraw_callback
	self._event_callback = event_callback
	self._resize_callback = resize_callback
	
        self.size = (100, 100)
	
	self._ctrl = gtk.DrawingArea()
	self._ctrl.set_size_request(100, 100)
	self._ctrl.connect("expose-event", self._OnExpose)
	self._ctrl.connect('key_press_event', self._OnKeyPress)
	self._ctrl.connect('key_release_event', self._OnKeyRelease)
        self._ctrl.connect('size-allocate', self._OnResize)
	self._ctrl.connect('hierarchy-changed', self._OnReparent)
        self._cm = self._ctrl.get_colormap()
        self._layout = self._ctrl.create_pango_layout("")

        self._ctrl.show()
        
        
    def line(self, coordseq, outline=None, fill=None, width=None, pattern=None):
        """Draws a line connecting the points in the given coordinate sequence."""
        gc = self._getGC(outline, width, pattern)
        self_ctrl.window.draw_lines(gc, self._convCoord(coordseq))

    def polygon(self, coordseq, outline=None, fill=None, width=None, pattern=None):
        """Draws a line connecting the points in the given coordinate sequence, 
           and additionally draws an extra line connecting the first and the last point 
           in the sequence. If a fill color or pattern is specified,
           the polygon is filled with that color or pattern. 
        """
	if fill != None:
            gc = self._getGC(fill, width, pattern)
            self._ctrl.window.draw_polygon(gc, True, self._convCoord(coordseq))
	
	if outline != None:
	    gc = self._getGC(outline, width, pattern)
            self._ctrl.window.draw_polygon(gc, False, self._convCoord(coordseq))

    def rectangle(self, coordseq, outline=None, fill=None, width=None, pattern=None):
        """Draws rectangles between pairs of coordinates in the given sequence.
            The coordinates specify the top-left and the bottom- right corners of 
            the rectangle. The sequence must have an even number of coordinates. 
        """
	coords = self._convCoord(coordseq)\
	
	if fill != None:
            gcf = self._getGC(fill, width, pattern)
	if outline != None:
            gco = self._getGC(outline, width, pattern)
        
	for i in range(len(coords) / 2):
	    if fill != None:
                self._ctrl.window.draw_rectangle(gcf, True, 
                        coords[i][0], coords[i][1], 
                        coords[i+1][0], coords[i+1][1])
	    if outline != None:
                self._ctrl.window.draw_rectangle(gco, False, 
                        coords[i][0], coords[i][1], 
                        coords[i+1][0], coords[i+1][1])

    def ellipse(self, coordseq, outline=None, fill=None, width=None, pattern=None):
        """Draws ellipses between pairs of coordinates in the given sequence. 
           The coordinates specify the top-left and bottom-right corners of the 
           rectangle inside which the ellipse is contained. The sequence must have 
           an even number of coordinates. 
        """
	coords = self._convCoord(coordseq)
	
	if fill != None:
            gcf = self._getGC(fill, width, pattern)
	if outline != None:
            gco = self._getGC(outline, width, pattern)
	
	for i in range(len(coords) / 2):
	    if fill != None:
                self._ctrl.window.draw_arc(gcf, True,
                        coords[i][0], coords[i][1], 
                        coords[i+1][0] - coords[i][0],
                        coords[i+1][1] - coords[i][1],
                        0, 360*64) 
	    if outline != None:
                self._ctrl.window.draw_arc(gco, False, 
                        coords[i][0], coords[i][1], 
                        coords[i+1][0] - coords[i][0],
                        coords[i+1][1] - coords[i][1],
                        0, 360*64) 

    def pieslice(self, coordseq, start, end, outline=None, fill=None, width=None, pattern=None):
        """Draws pie slices contained in ellipses between pairs of coordinates in the tgiven sequence. 
           The star and end parameters are floats that specify the start and end points of pie slice 
           as the starting and ending angle in radians. The angle 0 is to the right, the angle pi/2 
           is straight up, pi is to the left and-pi/2 is straight down. coordseq is interpreted the
           same way as for the ellipse method. 
        """
	coords = self._convCoord(coordseq)
        astart = self._convAng(start) 
        aend = self._convAng(end) - astart
	
	if fill != None:
            gcf = self._getGC(fill, width, pattern)
	if outline != None:
            gco = self._getGC(outline, width, pattern)
	
	for i in range(len(coords) / 2):
	    if fill != None:
                self._ctrl.window.draw_arc(gcf, True, 
                        coords[i][0], coords[i][1], 
                        coords[i+1][0] - coords[i][0],
                        coords[i+1][1] - coords[i][1],
                        astart, aend)
    	    if outline != None:
                self._ctrl.window.draw_arc(gco, False, 
                        coords[i][0], coords[i][1], 
                        coords[i+1][0] - coords[i][0],
                        coords[i+1][1] - coords[i][1],
                        astart, aend)

    def arc(self, coordseq, start, end, outline=None, fill=None, width=None, pattern=None):
        """Draws arcs contained in ellipses between pairs of coordinates in the given sequence. 
           The start and end parameters are floats that specify the start and end points of 
           pie slice as the starting and ending angle in radians. The angle 0 is to the right, 
           the angle pi/2 is straight up, pi is to the left and-pi/2 is straight down. coordseq 
           is interpreted the same way as for the ellipse method. 
        """
	coords = self._convCoord(coordseq)
        astart = self._convAng(start) 
        aend = self._convAng(end) - astart
	
	if fill != None:
            gcf = self._getGC(fill, width, pattern)
	if outline != None:
            gco = self._getGC(outline, width, pattern)
	
	for i in range(len(coords) / 2):
	    if fill != None:
                self._ctrl.window.draw_arc(gcf, True, 
                        coords[i][0], coords[i][1], 
                        coords[i+1][0] - coords[i][0],
                        coords[i+1][1] - coords[i][1],
                        astart, aend)
	    if outline != None:
                self._ctrl.window.draw_arc(gco, False, 
                        coords[i][0], coords[i][1], 
                        coords[i+1][0] - coords[i][0],
                        coords[i+1][1] - coords[i][1],
                        astart, aend)

    def point(self, coordseq, outline=None, fill=None, width=None, pattern=None):
        """Draws points in each coordinate in the given coordinate sequence. 
           If the width option is set to greater than 1, draws a crude approximation 
           of a circle filled with the outline color in the locations.
           Note that the approximation is not very accurate for large widths; 
           use the ellipse method if you need a precisely formed circle. 
        """
        gc = self._getGC(outline, width, pattern)
        self._ctrl.window.draw_points(gc, self._convCoord(coordseq))
     
    def clear(self, color=(255,255,255)):
        """Sets the entire surface of the drawable to the given color, white by default."""
        gc = self._getGC(color, None, None)
        sz = self._ctrl.window.get_size()
        self._ctrl.window.draw_rectangle(gc, True, 0, 0, sz[0], sz[1])

    def text(self, coordseq, text, fill=0, font=u"LatinBold12"):
        """Draws the given text in the points in the given coordinate sequence with the 
           given color (default value is black) and the given font. 
           The font specification format is described above.
        """
        self._layout.set_text(text)
        gc = self._getGC(fill, None,None)
        coord = self._convCoord(coordseq)
        for i in coord:
            self._ctrl.window.draw_layout(gc, i[0], i[1],self._layout)
            #self._ctrl.window.draw_text(font, gtc, i[0], i[1], text)


#    def blit(self, image, target=(0,0), source=((0,0), image.size), mask=None, scale=0):
#        """Copies the source area from the given image to the target area in this drawable."""
#        pass
 
    def _getGC(self, color=None, width=None, pattern=None):
        gc = self._ctrl.window.new_gc()
        gc.set_foreground(self._toGDKColor(color))
        if width:
            gc.set_line_width(width)
        if pattern:
            pass
        return gc

    def _toGDKColor(self, color):
        __colorconv = 65535 / 255 
        if isinstance(color, tuple):
	    r = color[0] * __colorconv
            g = color[1] * __colorconv
            b = color[2] * __colorconv
        else:
	    r = ((color & 0xFF0000) >> 16) * __colorconv
            g = ((color & 0x00FF00) >> 8) * __colorconv
            b = (color & 0x0000FF) * __colorconv
        return self._cm.alloc_color(r,g,b)

    def _convCoord(self, coordlst):
        if isinstance(coordlst[0], tuple):
            return coordlst
        else:
            return coordlst

    def _convAng(self, a):
        ang = math.floor(a * 180.0 / 3.14)
        if ang < 0:
            ang = 360 + ang
        return int(ang * 64)

    def _OnExpose(self, widget, event):
	if self._redraw_callback is not None:
            self._redraw_callback((0,0,self.size[0], self.size[1]))

    def _OnKeyPress(self, widget, event, data = None):
        if self._event_callback is not None:
            key = None
            scan = None
            mod = None
            data = {
                'type':EEventKeyDown,
                'keycode':key,
                'scancode': scan,
                'modifiers':mod
                }
            self._event_callback(data)

            data['type'] = EEventKey
            self._event_callback(data)

    def _OnKeyRelease(self, widget, event, data = None):
        if self._event_callback is not None:
            key = None
            scan = None
            mod = None
            data = {
                'type':EEventKeyUp,
                'keycode':key,
                'scancode': scan,
                'modifiers':mod
                }
            self._event_callback(data)

    def _OnResize(self, widget, rect):
        self.size = (rect.width, rect.height)
        if self._resize_callback is not None:
            self._resize_callback(self.size)
	    
    def _OnReparent(self, widget, prev, data = None):
        self._d = self._ctrl.window
	print "parented " +str(self._d)
        
            
    
    
